package com.websuites.proxy;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.util.Enumeration;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.websuites.core.request.WsHttpServletRequest;
import com.websuites.core.response.WsHttpServletResponse;
import com.websuites.utils.LogUtil;
import com.websuites.utils.StringUtil;

@SuppressWarnings("deprecation")
public class HttpProxyServlet extends HttpServlet
{	
	private static final long serialVersionUID = 3958519176057925967L;
	
	protected int maxPostSize = -1;
	protected boolean fileCaching = true;
	protected String pathTrim;
	protected String pathPrepend;
	public static final int MAX_POST_IN_MEMORY = Chunk.CHUNK_SIZE;

	private ConnectionPool connectionPool = null;
	private HttpProxyThread httpProxyThread = null;

	public void init(ServletConfig servletConfig) throws ServletException
	{
		super.init(servletConfig);	
		
		ConnectionParam connectionParam = new ConnectionParam();
		
	    String redirectURL = getInitParameter("redirectURL");
	    connectionParam.setRedirectURL(redirectURL);
	    
	    try 
	    {
		    URL localURL = new URL(redirectURL);
	        String destHost = localURL.getHost();
	        int destPort = localURL.getPort();
	        if (destPort == -1) 
	    	{
	        	destPort = 80; 
	    	}
	        connectionParam.setHost(destHost);
	        connectionParam.setPort(destPort);
	    }
	    catch (MalformedURLException localMalformedURLException) 
	    {
	    	throw new ServletException("Bad redirectURL - " + localMalformedURLException.getMessage());
	    }  
	    
	    this.pathTrim = getInitParameter("PathTrim");
	    if (this.pathTrim == null) 
	    {
	    	this.pathTrim = getInitParameter("pathTrim");
	    }
	    if ((this.pathTrim != null) && (this.pathTrim.charAt(0) != '/'))
	    {
	    	this.pathTrim = ("/" + this.pathTrim);
	    }

	    this.pathPrepend = getInitParameter("PathPrepend");
	    if (this.pathPrepend == null) 
	    {
	    	this.pathPrepend = getInitParameter("pathPrepend");
	    }

	    String maxPostSize = getInitParameter("MaxPostSize");
	    if (maxPostSize != null) 
	    {
	    	this.maxPostSize = Integer.parseInt(maxPostSize);
	    }

	    String fileCaching = getInitParameter("FileCaching");
	    if (fileCaching != null)
	    {
		    if (fileCaching.equalsIgnoreCase("ON") || fileCaching.equalsIgnoreCase("true"))
		    {
		    	this.fileCaching = true;
		    }	
		    else
		    {
		    	this.fileCaching = false;
		    }
	    }

	    String keepAliveEnabled = getInitParameter("KeepAliveEnabled");
	    if (keepAliveEnabled != null)
	    {
		    if (keepAliveEnabled.equalsIgnoreCase("ON") || keepAliveEnabled.equalsIgnoreCase("true"))
		    {
		    	connectionParam.setKeepAliveEnabled(true);
		    }
		    else
		    {
		    	connectionParam.setKeepAliveEnabled(false);
		    }
	    }
	    
	    String maxPoolSize = getInitParameter("MaxPoolSize");
	    if (maxPoolSize != null)
	    {
	    	connectionParam.setMaxPoolSize(Integer.parseInt(maxPoolSize));
	    }
	    
	    String minPoolSize = getInitParameter("MinPoolSize");
	    if (minPoolSize != null)
	    {
	    	connectionParam.setMinPoolSize(Integer.parseInt(minPoolSize));
	    }
	    String keepAliveSecs = getInitParameter("KeepAliveSecs");
	    if (keepAliveSecs != null)
	    {
	    	connectionParam.setKeepAliveSecs(Integer.parseInt(keepAliveSecs));
	    }

	    String socketTimeoutSecs = getInitParameter("SocketTimeoutSecs");
	    if (socketTimeoutSecs != null)
	    {
	    	connectionParam.setKeepAliveSecs(Integer.parseInt(socketTimeoutSecs));
	    }
	     
	    connectionPool = new ConnectionPool(connectionParam);
	    
	    httpProxyThread = new HttpProxyThread(connectionPool);
	    try 
	    {
			httpProxyThread.startThread();
		} 
	    catch (IOException e) 
	    {
			e.printStackTrace();
		}	
	}
	
	public void destroy()
	{
	    if (this.connectionPool != null)
	    {
	    	try 
	    	{
				this.connectionPool.destroyPool();
			} 
	    	catch (IOException e)
	    	{
				e.printStackTrace();
			}
	    }
	    if (this.httpProxyThread != null)
	    {
	    	this.httpProxyThread.stop();
	    	this.httpProxyThread.destroy();
	    }
	}
	
	public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException
	{
		processRequest(request, response, "GET");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException
	{		
		processRequest(request, response, "POST");
	}

	protected void processRequest(HttpServletRequest request, HttpServletResponse response, String method) throws ServletException, IOException
	{		    
	    PostDataCache postDataCache = new PostDataCache();
	    postDataCache.readPostData(request);
	    if (postDataCache.error)
	    {
	    	postDataCache.release();
	    	throw new IOException("Invalid Post");
	    }

		Connection proxyConn = this.connectionPool.getConnection();
		proxyConn.setLastUsed(System.currentTimeMillis());
		
//		String sURL = request.getRequestURL().toString();
//		LogUtil.info("Get Sokcet:" + sURL + "," + proxyConn.getSocket().toString());
		
		try
		{
			try
			{
				sendRequest(request, proxyConn, postDataCache);
				sendResponse(request, response, proxyConn, postDataCache);
				this.connectionPool.releaseConnection(proxyConn);
//				LogUtil.info("Release Socket:" + sURL + "," + proxyConn.getSocket().toString());
			}
			catch (HalfOpenSocketRetryException localHalfOpenSocketRetryException) 
			{	
				this.connectionPool.removeConnection(proxyConn);
		        if (!response.isCommitted()) 
		        {
		        	response.sendError(503, "Unable to connect to server");
		        }
		        localHalfOpenSocketRetryException.printStackTrace();
			}
		}
		catch (WriteClientIOException localWriteClientIOException)
		{
			this.connectionPool.releaseConnection(proxyConn);
		}
	    catch (IOException localIOException)
	    {
	        if (proxyConn != null) 
	        {
	            this.connectionPool.removeConnection(proxyConn);
	            if (!response.isCommitted())
	            {
	            	response.sendError(500, "Internal Server Error");
	            }
	        }
	        else
	        {
	        	response.sendError(503, "Unable to connect to server");
	        }    	
            localIOException.printStackTrace();
	    }
	    finally
	    {
	    	postDataCache.release();
	    }	    	
	}
	
	public void test(WsHttpServletRequest request, WsHttpServletResponse response, String method) throws ServletException, IOException
	{
		Socket sock = new Socket("192.168.130.225", 6088);
		sock.setSoTimeout(300000);
		sock.setTcpNoDelay(true);
		
		StringBuilder sRequest = new StringBuilder(512);
		sRequest.append("GET http://192.168.130.225:6088/ys/index.jsp HTTP/1.1\r\n");
		sRequest.append("Accept: */*\r\n");
		sRequest.append("Accept-Language: zh-cn\r\n");
		sRequest.append("User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\n");
		sRequest.append("Host: 192.168.130.225:6088\r\nConnection: Close\r\n\r\n");
	
		BufferedOutputStream remoteBufferedOutputStream = new BufferedOutputStream(sock.getOutputStream());
		PrintStream remotePrintStream = new PrintStream(remoteBufferedOutputStream);
		remotePrintStream.print(sRequest.toString());	
		remotePrintStream.flush();

	    DataInputStream remoteDataInputStream = new DataInputStream(new BufferedInputStream(sock.getInputStream()));
		String str = null;                                                                                                                                                                             
		try 
		{                                                                                                                                                                                          
			str = remoteDataInputStream.readLine();   
			LogUtil.info(str);
		} 
		catch (IOException localIOException) 
		{                                                                                                                                                         
			throw localIOException;                                                                                                                                                                      
		}                                                                                                                                                                                              		
	}
	public void sendRequest(HttpServletRequest request, Connection proxyConn, PostDataCache postDataCache) throws IOException
	{
		BufferedOutputStream remoteBufferedOutputStream = new BufferedOutputStream(proxyConn.getSocket().getOutputStream());
		PrintStream remotePrintStream = new PrintStream(remoteBufferedOutputStream);
	    
		String uri = resolveRequest(request);
					    
	    InputStream localInputStream = null;
	    if (postDataCache != null) 
	    {
	    	localInputStream = postDataCache.getInputStream();
	    }

	    Chunk localChunk = Chunk.getChunk();	    
	    try
	    {
	    	remotePrintStream.print(uri);		
		    sendRequestHeaders(request, proxyConn, remotePrintStream);

		    int contentLength = request.getContentLength();
		    InputStream localServletInputStream = localInputStream != null ? localInputStream : request.getInputStream();
		    if (contentLength > 0)
		    {   
		        localChunk = Chunk.getChunk();
		        int readLength = 0;
		        while ((readLength = localServletInputStream.read(localChunk.getBuffer(), 0, contentLength)) > 0) 
		        {	    	
		        	try 
		        	{
		        		remoteBufferedOutputStream.write(localChunk.getBuffer(), 0, readLength);
		        		remoteBufferedOutputStream.flush();
		            } 
		        	catch (IOException localIOException) 
		        	{
		                throw localIOException;
		            }
		        	contentLength -= readLength;
		            if (contentLength > 0) 
		            {
		            	continue;	        	
		            }
		            else
		            {
		            	break;
		            }
		        }
		        if (contentLength != 0) 
		        {
		            throw new IOException("Failed to read " + request.getContentLength() + " bytes from the inputStream");
		        }
		    }
		    else if (contentLength == -1)
		    {
		        String transferEncoding = request.getHeader("Transfer-Encoding");
		        if ((transferEncoding != null) && (transferEncoding.equalsIgnoreCase("Chunked")))
		        {
		        	throw new IOException("Can't process chunked request.");
		        }
		    }		    
	    }
	    finally 
	    {
	        if (localChunk != null)
	        {
	        	Chunk.free(localChunk);
	        }
	    }
	}

	@SuppressWarnings("unchecked")
	protected void sendRequestHeaders(HttpServletRequest request, Connection proxyConn, PrintStream remotePrintStream) throws IOException
	{		
	    StringBuilder localStringBuilder = new StringBuilder(256);
	    Enumeration en = request.getHeaderNames();
	    while (en.hasMoreElements())
        {
           String sHeaderName = (String)en.nextElement();
           if (sHeaderName != null)
           {
	           String sClientHeaderValue = request.getHeader(sHeaderName);
//	           LogUtil.info("client requst header, " + sHeaderName + ":" + sClientHeaderValue);

	           if (sClientHeaderValue == null)
	           {
	        	   continue;
	           }
	           if ("Expect".equalsIgnoreCase(sHeaderName))
	           {
	        	   continue;
	           }
	           if ("Transfer-Encoding".equalsIgnoreCase(sHeaderName))
	           {
	        	   continue;
	           }
	           if ("Host".equalsIgnoreCase(sHeaderName))
	           {
	        	   continue;
	           }
	           if ("Connection".equalsIgnoreCase(sHeaderName))
	           {
	        	   continue;
	           }
	           
	           byte[] arrayOfByte = sClientHeaderValue.getBytes();
	           String sProxyHeaderValue;
	           try
	           {
	        	   String charsetName = request.getCharacterEncoding();
	        	   if (charsetName != null)
	        	   {
	        		   sProxyHeaderValue = new String(arrayOfByte, charsetName);
	        	   }
	        	   else
	        	   {
	        		   sProxyHeaderValue = new String(arrayOfByte);
	        	   }
	           }
	           catch (UnsupportedEncodingException localUnsupportedEncodingException) 
	           {
	        	   sProxyHeaderValue = new String(arrayOfByte);
	           }
	           localStringBuilder.append(sHeaderName).append(": ").append(sProxyHeaderValue);	           
//	           LogUtil.info("proxy request header " + request.getRequestURI() + ", " + localStringBuilder.toString());
	           localStringBuilder.append("\r\n");
	           remotePrintStream.print(localStringBuilder.toString());
	           localStringBuilder.delete(0, localStringBuilder.length());
           	}         
        }
	    
		ConnectionParam connectionParam = this.connectionPool.getConnectionParam();
//	    LogUtil.info("proxy request header,  Host: " + connectionParam.getHost() + ":" + connectionParam.getPort());
	    remotePrintStream.print("Host: " + connectionParam.getHost() + ":" + connectionParam.getPort());		
	    remotePrintStream.print("\r\n");
	    remotePrintStream.print("Proxy-Host: " + request.getServerName() + ":" + request.getServerPort() + ":" + request.getContextPath());		
//	    LogUtil.info("Proxy-Host: " + request.getServerName() + ":" + request.getServerPort() + ":" + request.getContextPath());
	    remotePrintStream.print("\r\n");
	    
//	    LogUtil.info("proxy request header,  Connection: Keep-Alive");
	    remotePrintStream.print("Connection: Keep-Alive");
	    remotePrintStream.print("\r\n");
	   
	    remotePrintStream.print("\r\n");
	    remotePrintStream.flush();
	}
		
	protected String resolveRequest(HttpServletRequest request)
	{
		String uri = (String)request.getAttribute("javax.servlet.include.request_uri");
		if (uri == null)
		{
			uri = request.getRequestURI();
		}
//		LogUtil.info("client request, " + uri);
		
		if (this.pathTrim != null) 
		{
	        int i = uri.indexOf(this.pathTrim);
	        if (i > -1) 
	        {
	        	int j = this.pathTrim.length();
	        	uri = uri.substring(0, i) + uri.substring(i + j);
	        }
	    }

	    uri = this.pathPrepend + uri;

	    String query = request.getQueryString();

	    if ((query != null) && (!query.equals(""))) 
	    {
	    	uri = uri + "?" + query;
	    }

	    StringBuilder remoteHttp = new StringBuilder(256);
	    remoteHttp.append(request.getMethod());
	    remoteHttp.append(" ");
	    remoteHttp.append(uri);
	    remoteHttp.append(" ");
	    remoteHttp.append(request.getProtocol());
//		LogUtil.info("proxy request, " + remoteHttp.toString());
	    remoteHttp.append("\r\n");
	    return remoteHttp.toString();
	}
	
	public void sendResponse(HttpServletRequest request, HttpServletResponse response, Connection proxyConn, PostDataCache postDataCache) throws IOException
	{    
	    DataInputStream remoteDataInputStream = new DataInputStream(new BufferedInputStream(proxyConn.getSocket().getInputStream(), 100));
		
	    int statusCode = readStatus(request, response, remoteDataInputStream, proxyConn.isCanRecycle());
	    int contentLength = sendResponseHeaders(request, response, remoteDataInputStream, proxyConn);
	    if (statusCode == 100)  //Continue
	    {
	    	statusCode = readStatus(request, response, remoteDataInputStream);
	    	contentLength = sendResponseHeaders(request, response, remoteDataInputStream, proxyConn);
	    }

	    //204 No Content  304 Not Modified
	    if ((statusCode == 204) || (statusCode == 304) || (contentLength == 0) || ("HEAD".equalsIgnoreCase(request.getMethod())))
	    {
	        return;
	    }
	    ServletOutputStream servletOutputStream = response.getOutputStream();
	    if (contentLength == -9999)
	    {
	    	if (ChunkInput.readCTE(servletOutputStream, remoteDataInputStream) == -1) 
	    	{
	    		proxyConn.setCanRecycle(false); 
	    	}
	    }
	    else
	    {
	    	readAndWriteResponseData(remoteDataInputStream, servletOutputStream, contentLength);   
	    }
	}
	
	void readAndWriteResponseData(DataInputStream remoteDataInputStream, OutputStream servletOutputStream, int contentLength) throws IOException
	{
	    int i = 1;
	    Chunk localChunk = Chunk.getChunk();
	    try
	    {
	        int j;
	        if (contentLength > 0)
	        {
	            int k = contentLength;
	            while ((j = remoteDataInputStream.read(localChunk.getBuffer(), 0, Math.min(k, localChunk.getBufferSize()))) != -1) 
	            {
	            	if (i != 0) 
	            	{
	            		try 
	            		{
	            			servletOutputStream.write(localChunk.getBuffer(), 0, j);
	            		} 
	            		catch (IOException localIOException2) 
	            		{
	            			i = 0;
	            			throw new WriteClientIOException("Error in writing to client");
	            		}
	            	}
	            	k -= j;
	            	if (k < 1)
	            	{
	            		break;
	            	}
	            }
	            if (k != 0)
	            {
	            	throw new IOException("Failed to read " + contentLength + " bytes from the inputStream");
	            }	        	
	        }
	        else
	        {
	            while ((j = remoteDataInputStream.read(localChunk.getBuffer())) != -1)
	            {
	                if (i == 0) continue;
	                try
	                {
	                	servletOutputStream.write(localChunk.getBuffer(), 0, j);
	                	servletOutputStream.flush();
	                } 
	                catch (IOException localIOException1)
	                {
	                	i = 0;
	                  	throw new WriteClientIOException("Error in writing to client");
	                }
	            }	        	
	        }
	    }
	    finally
	    {
	    	Chunk.free(localChunk);
	    }	
	}

	protected int readStatus(HttpServletRequest request, HttpServletResponse response, DataInputStream remoteDataInputStream) throws IOException
	{   
		return readStatus(request, response, remoteDataInputStream, false);
	}
	
	protected int readStatus(HttpServletRequest request, HttpServletResponse response, DataInputStream remoteDataInputStream, boolean canRecycle) throws IOException
	{                                                                                                                                                                                                
		String status = null;                                                                                                                                                                             
		try 
		{                                                                                                                                                                                          
			status = remoteDataInputStream.readLine();
//			LogUtil.info("server response start line, " + status);
		} 
    	catch (SocketException localSocketException)
    	{
    		if (canRecycle) 
    		{
    			throw new HalfOpenSocketRetryException(localSocketException); 
    		}
    	}
		catch (IOException localIOException) 
		{                                                                                                                                                         
			throw localIOException;                                                                                                                                                                      
		}                                                                                                                                                                                              
		if (status == null) 
		{                                                                                                                                                                             
    		if (canRecycle) 
    		{
    			throw new HalfOpenSocketRetryException("status line is null"); 
    		}
			throw new IOException("status line is null");                                                                                                                                                
		}                                                                                                                                                                                              
		int statusCode = 500;                                                                                                                                                           
		try 
		{                                                                                                                                                                                          
			String[] arrayOfString = StringUtil.split(" ", status);                                                                                                                              
			statusCode = Integer.parseInt(arrayOfString[1]);                                                                                                                                                      
			response.setStatus(statusCode);                                                                                                                                                       
		} 
		catch (IndexOutOfBoundsException localIndexOutOfBoundsException) 
		{                                                                                                                           
			throw new IOException("malformed status line");                                                                                                                                              
		}                                                                                                                                                                                              
		return statusCode;                                                                                                                                                                                      
	}  
	
	protected int sendResponseHeaders(HttpServletRequest request, HttpServletResponse response, DataInputStream remoteDataInputStream, Connection proxyConn) throws IOException
	{
	    int contentLength = -1;
	    
	    String line;
	    while (((line = remoteDataInputStream.readLine()) != null) && (line.length() > 0))
	    {
	    	remoteDataInputStream.mark(1);
	        int k = remoteDataInputStream.read();
	        remoteDataInputStream.reset();
	        if ((k == 32) || (k == 9)) 
	        {
	        	do 
	        	{
	        		line = line + "\r\n" + remoteDataInputStream.readLine();
	        		remoteDataInputStream.mark(1);
	        		k = remoteDataInputStream.read();
	        		remoteDataInputStream.reset();
	        	}while ((k == 32) || (k == 9));
	        } 
	        
	        String[] arrayOfString = StringUtil.split(":", line);
	        String headerName = arrayOfString[0].trim();
	        String headerValue = arrayOfString[1].trim();
//	        LogUtil.info("server response header, " + headerName + ":" + headerValue);

	        if (headerName.equalsIgnoreCase("Set-Cookie"))
	        {
	        	StringBuilder headerCookieValue = new StringBuilder();
		        String[] arrayOfCookies = StringUtil.split(";", headerValue);
		        for (int i = 0; i < arrayOfCookies.length; i++)
		        {	        	
		        	 String[] arrayOfCookie = StringUtil.split("=", arrayOfCookies[i].trim());
		        	 String cookieName = arrayOfCookie[0].trim();
		        	 String cookieValue = arrayOfCookie[1].trim();		        	 
		        	 if (cookieName.equalsIgnoreCase("Path"))
		        	 {
//		        		 cookieValue = request.getContextPath() + cookieValue;
		        		 cookieValue = request.getContextPath() + "/";
		        	 }
		        	 
		        	 if (headerCookieValue.length() > 0)
		        	 {
		        		 headerCookieValue.append(";");
		        	 }
		        	 headerCookieValue.append(cookieName);
		        	 headerCookieValue.append("=");
		        	 headerCookieValue.append(cookieValue);	        	 
		        }
	        	
	        	response.addHeader(headerName, headerCookieValue.toString());
//		        LogUtil.info("proxy response header, " + headerName + ":" + headerCookieValue.toString());
	        	continue;
	        }
	        if ((headerName.equalsIgnoreCase("Transfer-Encoding")) && (headerValue.equalsIgnoreCase("chunked")))
	        {
	        	contentLength = -9999;
	        	continue;
	        }
	        if (headerName.equalsIgnoreCase("Content-Length"))
	        {
	        	if (contentLength != -9999) 
	        	{
	        		contentLength = Integer.parseInt(headerValue);
	        	}
	        	response.addHeader(headerName, headerValue);
//		        LogUtil.info("proxy response header, " + headerName + ":" + headerValue);
	        	continue;
	        }
	        if (headerName.equalsIgnoreCase("connection")) 
	        {
	        	if (headerValue.equalsIgnoreCase("close"))
	        	{
	        		proxyConn.setCanRecycle(false);
	        		continue;
	        	}
		        proxyConn.setCanRecycle(true);
	            continue;
	        }
	        
	        response.addHeader(headerName, headerValue);
//	        LogUtil.info("proxy response header, " + headerName + ":" + headerValue);
	    }
	    return contentLength;
	}
	
	protected Chunk readPostDataToMemory(HttpServletRequest request, int paramInt) throws IOException
	{
		Chunk localChunk = Chunk.getChunk();
		try 
		{
			localChunk.read(request.getInputStream(), paramInt);
		} 
		catch (IOException localIOException) 
		{
			Chunk.free(localChunk);
			throw localIOException;
		}
		if (paramInt != localChunk.size()) 
		{
			Chunk.free(localChunk);
			return null;
		}
		return localChunk;
	}

	protected File readPostDataToFile(HttpServletRequest request, int paramInt) throws IOException
	{
		File localFile = null;
		FileOutputStream localFileOutputStream = null;
		Chunk localChunk = Chunk.getChunk();
		try 
		{
			localFile = File.createTempFile("proxy", null, null);
			if (localFile == null) 
			{ 
				return localFile;
			}
			localFileOutputStream = new FileOutputStream(localFile);
	
			ServletInputStream localServletInputStream = request.getInputStream();
	
			while (paramInt > 0) 
			{
				int i = localServletInputStream.read(localChunk.getBuffer(), 0, localChunk.getBufferSize());
		
				if (i < 0) 
				{
					try
					{
						if (localFileOutputStream != null) 
						{
							localFileOutputStream.close();
						}
						if (localFile != null) 
						{
							localFile.delete(); 
						}
					}
					catch (Exception localException2)
					{
					}
					return localFile;
				}
				localFileOutputStream.write(localChunk.getBuffer(), 0, i);
				paramInt -= i;
			}
			localFileOutputStream.flush();
			localFileOutputStream.close();
		} 
		catch (IOException localIOException) 
		{
			try
			{
				if (localFileOutputStream != null) localFileOutputStream.close();
				if (localFile != null) localFile.delete(); 
			} 
			catch (Exception localException1) 
			{
			}
			throw localIOException;
		} 
		finally 
		{
			Chunk.free(localChunk);
		}
		return localFile;
	}
	
	public class PostDataCache 
	{
	    protected static final int NO_CACHE = 0;
	    protected static final int MEMORY_CACHE = 1;
	    protected static final int FILE_CACHE = 2;
	    protected int type = 0;
	    protected boolean error;
	    private Chunk postData;
	    private File postDataFile;
	    private boolean alreadyRead;

	    PostDataCache(){}

	    public void readPostData(HttpServletRequest request)
	    {
		    if (!this.alreadyRead) 
		    {
			    int size = request.getContentLength();
			    try
			    {
				    if ((HttpProxyServlet.this.maxPostSize > 0) && (size > HttpProxyServlet.this.maxPostSize)) 
				    {
					    this.error = true;
					}
				    else if ((size > 0) && (size <= HttpProxyServlet.MAX_POST_IN_MEMORY)) 
				    {
					    this.type = MEMORY_CACHE;
					    this.postData = HttpProxyServlet.this.readPostDataToMemory(request, size);
					    if (this.postData == null) 
					    {
					    	this.error = true; 
					    }
					}
					else if ((size > HttpProxyServlet.MAX_POST_IN_MEMORY) && (HttpProxyServlet.this.fileCaching)) 
					{
					    this.type = FILE_CACHE;
					    this.postDataFile = HttpProxyServlet.this.readPostDataToFile(request, size);
					    if (this.postDataFile == null) 
					    {
					    	this.error = true; 
					    }
				    }
			    }
			    catch (IOException localIOException) 
			    {
				    this.error = true;
			    }
			    finally 
			    {
			    	this.alreadyRead = true;
			    }
		    }
	    }

	    public InputStream getInputStream() throws IOException
	    {
	    	if (this.error) 
	    	{
	    		return null;
	    	}
		    Object localObject = null;
		    switch (this.type) 
		    {
		    case NO_CACHE:
		    	break;
		    case MEMORY_CACHE:
		    	localObject = new ByteArrayInputStream(this.postData.getBuffer(), 0, this.postData.getBufferSize());
		    	break;
		    case FILE_CACHE:
		    	localObject = new FileInputStream(this.postDataFile);
		    	break;
		    }

		    return (InputStream)localObject;
	    }

	    public void release()
	    {
		    switch (this.type)
		    {
		    case NO_CACHE:
		    	break;
		    case MEMORY_CACHE:
		    	Chunk.free(this.postData);
		    	break;
		    case FILE_CACHE:
		    	if (this.postDataFile != null)
		    	{
		    		this.postDataFile.delete();
		    	}
		    	break;
		    }
	    }
	}
}